package com.dhp.bkOjjudgeservice.codesandbox.impl;

import cn.hutool.core.io.FileUtil;
import com.dhp.bkOjjudgeservice.codesandbox.CodeSandBox;
import com.dhp.bkOjjudgeservice.codesandbox.utils.ProcessUtil;
import com.dhp.common.ErrorCode;
import com.dhp.exception.BusinessException;
import com.dhp.model.codesandbox.ExecuteMessage;
import com.dhp.model.codesandbox.ExecuteRequest;
import com.dhp.model.codesandbox.ExecuteResponse;
import com.dhp.model.codesandbox.JudgeInfo;
import com.dhp.model.enums.CodeSandBoxExecuteEnum;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * 示例代码沙箱(跑通业务流程)
 */
@Service
public class ExampleCodeSandBox implements CodeSandBox {

    //用户代码存放文件夹
    private static final String userTempCode = "src/main/resources/tempCode";
    //代码文件名
    private static final String codeFileName = "Main.java";

    /**
     * 执行代码沙箱
     *
     * @param executeRequest
     * @return
     */
    @Override
    public ExecuteResponse execute(ExecuteRequest executeRequest) {

        String language = executeRequest.getLanguage();
        String code = executeRequest.getCode();
        List<String> inputList = executeRequest.getInputList();
        //运行时间 = 编译时间 + 最大执行时间
        long totalTime = 0;
        //最大执行时间
        long maxExecuteTime = 0;

        //获取用户所在根目录
        ExecuteResponse executeResponse = new ExecuteResponse();
        String userRoot = System.getProperty("user.dir");
        String tempCodePath = userRoot + File.separator + userTempCode + File.separator + UUID.randomUUID();

        //创建代码文件
        if(!FileUtil.exist(tempCodePath)){
            //创建文件夹
            FileUtil.mkdir(tempCodePath);
        }
        String userCodeFile = tempCodePath + File.separator + codeFileName;
        File file = FileUtil.writeString(code , userCodeFile , StandardCharsets.UTF_8);
        if (file == null){
            throw new BusinessException(ErrorCode.OPERATION_ERROR , "代码文件创建失败");
        }

        //编译代码文件
        try {
            StopWatch watch = new StopWatch();
            watch.start();
            String compileCommand = String.format("javac -encoding utf-8 %s" , file.getAbsolutePath());
            Process compileProcess = Runtime.getRuntime().exec(compileCommand);
            ExecuteMessage compileMessage = ProcessUtil.executeAndGetMsg(compileProcess, "编译");
            System.out.println(compileMessage);
            //如果编译有问题直接返回不执行后续代码
            if (StringUtils.isNotEmpty(compileMessage.getExecuteErrorMessage())){
                return getErrorResponse();
            }
            watch.stop();
            totalTime += watch.getLastTaskTimeMillis();
        } catch (Exception e) {
            ExecuteResponse errorResponse = getErrorResponse();
            return errorResponse;
        }

        List<ExecuteMessage> messageList = new ArrayList<>();
        //执行编译文件
        try {
            //执行编译好的class文件
            for (String args : inputList) {
                StopWatch watch = new StopWatch();
                watch.start();
                String runCommand = String.format("java -Dfile.encoding=UTF-8 -cp %s Main %s", tempCodePath, args);
                Process runProcess = Runtime.getRuntime().exec(runCommand);
                ExecuteMessage runMessage = ProcessUtil.executeAndGetMsg(runProcess, "执行");
                messageList.add(runMessage);
                watch.stop();
                maxExecuteTime = Math.max(watch.getLastTaskTimeMillis() , maxExecuteTime);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        List<String> output = new ArrayList<>();
        for (ExecuteMessage executeMessage : messageList) {
            if (StringUtils.isNotEmpty(executeMessage.getExecuteErrorMessage())){
                executeResponse.setStatus(CodeSandBoxExecuteEnum.EXE_FAILED.getValue());
                executeResponse.setMsg(executeMessage.getExecuteErrorMessage());
                break;
            }
            output.add(executeMessage.getExecuteMessage());
        }
        if (output.size() == messageList.size()){
            executeResponse.setOutputList(output);
            executeResponse.setMsg("ok");
            executeResponse.setStatus(CodeSandBoxExecuteEnum.EXE_SUCCESS.getValue());
        }
        //todo 先给假数据，之后考虑如何获取执行内存
        JudgeInfo judgeInfo = new JudgeInfo();
        judgeInfo.setMemory(0L);
        judgeInfo.setTime(totalTime + maxExecuteTime);
        executeResponse.setJudgeInfo(judgeInfo);

        //删除临时的代码文件
        boolean del = FileUtil.del(userCodeFile);
        if (!del){
            throw new BusinessException(ErrorCode.OPERATION_ERROR , "代码文件删除失败");
        }

        return executeResponse;
    }

    private ExecuteResponse getErrorResponse(){
        ExecuteResponse executeResponse = new ExecuteResponse();
        executeResponse.setOutputList(new ArrayList<>());
        executeResponse.setMsg("编译失败");
        executeResponse.setJudgeInfo(new JudgeInfo());
        // 2表示代码沙箱错误，比如编译错误
        executeResponse.setStatus(CodeSandBoxExecuteEnum.COMPILE_FAILED.getValue());
        return executeResponse;
    }

}
